#include "main.h"
#include "ring_buffer.h"
#include "string.h"

#define SUCCESS			1
#define ERROR		    0


/**
 * @brief	创建和初始化环形缓冲区
 * @param	pRB	: 指向环形缓冲区的实例
 * @param	pBuffer: 指向用于填充环形缓冲区的数据
 * @param   size: 缓冲区数据的数目
 * @return	>=0:成功 ; <0:失败
 */
int32_t RingBuf_Init(ring_buffer_t* pRB, uint8_t* buffer, uint32_t size )
{
	if(!pRB )return ERROR;
	if(!size)return ERROR;

	pRB->pBuf	 = (uint8_t*)buffer;
	pRB->size		 = size;
	pRB->rNdx		 = 0;
	pRB->wNdx		 = 0;
	pRB->cnt = 0;
	return SUCCESS;
}
/**
 * @brief	释放环形缓冲区
 * @param	pRB	: 指向环形缓冲区的实例
 * @return	>=0:成功 ; <0:失败
 */
int32_t RingBuf_Deinit(ring_buffer_t* pRB )
{
	if(!pRB )return ERROR;
	pRB = pRB;
	return SUCCESS;
}

/**
 * @brief	获取环形缓冲区剩余数量
 * @param	pRB	: 指向环形缓冲区的实例
 * @return	环形缓冲区剩余数量
 */
int32_t RingBuf_GetFreeBytes(ring_buffer_t* pRB )
{
	return pRB->size - pRB->cnt;
}
/**
 * @brief	获取环形缓冲区已经用了的数量
 * @param	pRB	: 指向环形缓冲区的实例
 * @return	环形缓冲区已用数量
 */
int32_t RingBuf_GetUsedBytes(ring_buffer_t* pRB)
{
	return pRB->cnt;
}

/**
 * @brief	写新数据到缓冲区
 * @param	pRB	: 指向环形缓冲区的实例
 * @param	pData: 指向要写入环形缓冲区的数据地址
 * @param   dataBytes: 要写入的字节数
 * @return	>=0:成功写入的字节 ; <0:写入失败
 * @remark  此功能会更新环形缓冲区
 */
int32_t RingBuf_Write(ring_buffer_t* pRB, const uint8_t* pdata, uint32_t dataBytes)
{
	if(!pRB )return ERROR;

	uint32_t writeToEnd, bytesToCopy;
	INIT_CRITICAL();
	ENTER_CRITICAL();
	/* 计算能写入的数量 */
	writeToEnd = pRB->size - pRB->wNdx;//从数据尾部标号的位置到缓冲区末尾的剩余空间 例如：-----111--(10-8=2)
	

	//要写入的数量和能写入的数量去最小的（实际能写入的数量）
	bytesToCopy = MIN(dataBytes, pRB->size - pRB->cnt);
	
	//容量和计数值相等时 bytesToCopy 为0，缓冲区已满。
	if (bytesToCopy != 0)
	{
		/* 先向环形缓冲区空间少的位置写入数据 */
		memcpy(&pRB->pBuf[pRB->wNdx], pdata, MIN(bytesToCopy, writeToEnd));

		/* Check if we have more to copy to the front of the buffer */
		if (writeToEnd < bytesToCopy)
		{
			memcpy(pRB->pBuf, pdata + writeToEnd, bytesToCopy - writeToEnd);
		}

		/* 更新数据标号 */
        //更新尾指针，对缓冲区的大小取余数（当达到最大值size时又从0开始），防止计数超限
		pRB->wNdx = (pRB->wNdx + bytesToCopy) % pRB->size;
        //更新计数值
		// pRB->cnt += dataBytes;
		pRB->cnt += bytesToCopy;
	}
	LEAVE_CRITICAL();
	return bytesToCopy;
}

/**
 * @brief	写一个新数据到缓冲区
 * @param	pRB	: 指向环形缓冲区的实例
 * @param	pcData: 指向要写入环形缓冲区的数据地址
 * @return	1:成功; 其它失败
 * @remark  此功能会更新环形缓冲区. 优先使用逐字节写入 
 */
int32_t RingBuf_Write1Byte(ring_buffer_t* pRB, const uint8_t *pcData)
{
	uint32_t ret = 0;
	if(!pRB )return ERROR;

	INIT_CRITICAL();
	ENTER_CRITICAL();
	if (pRB->cnt < pRB->size)
	{
		pRB->pBuf[pRB->wNdx] = pcData[0];
		pRB->wNdx = (pRB->wNdx + 1) % pRB->size;
		pRB->cnt++;
		ret = 1;
	}
	LEAVE_CRITICAL();
	return ret;
}

/**
 * @brief	从缓冲区读取数据
 * @param	pRB	: 指向环形缓冲区的实例
 * @param	pdata: 接收数据的地址
 * @param   dataBytes: 要读取的字节数
 * @param   isToFree: 1，读取并删除，0复制读取
 * @return	读取的字节数
 */
static int32_t _prvRingBuf_Read(ring_buffer_t* pRB, uint8_t* pdata, uint32_t dataBytes, uint32_t isToFree)
{
	uint32_t readToEnd, bytesToCopy;
	if(!pRB )return ERROR;
	
	INIT_CRITICAL();
	ENTER_CRITICAL();

	readToEnd = pRB->size - pRB->rNdx;//实际能读取的数量
	bytesToCopy = MIN(dataBytes, pRB->cnt);//要读出的不能大于计数值

	if (bytesToCopy != 0)
	{
		memcpy(pdata, &pRB->pBuf[pRB->rNdx], MIN(bytesToCopy, readToEnd));
		
		if (readToEnd < bytesToCopy)//读取的多于能读取的数量，即读已经到了写的前面(需要过零点)，111----111
            //从0开始读取剩余的
			memcpy(pdata + readToEnd, &pRB->pBuf[0], bytesToCopy - readToEnd);

		if (isToFree)
		{
            /* 更新数据标号 */
			pRB->rNdx = (pRB->rNdx + bytesToCopy) % pRB->size;
			pRB->cnt -= bytesToCopy;	
		}
	}
	LEAVE_CRITICAL();
	
	return bytesToCopy;
}

/**
 * @brief	从缓冲区取出后并删除缓冲区原有的数值，会更新数据指针
 * @param	pRB	: 指向环形缓冲区的实例
 * @param	pdata: 接收数据的地址
 * @param   dataBytes: 要读取的字节数
 * @return	读取的字节数
 */
int32_t RingBuf_Read(ring_buffer_t* pRB, uint8_t* pdata, uint32_t dataBytes)
{
	return _prvRingBuf_Read(pRB, pdata, dataBytes, 1);
}
/**
 * @brief	从缓冲区取出后，缓冲区原有的数值不变，不会更新数据指针
 * @param	pRB	: 指向环形缓冲区的实例
 * @param	pdata: 接收数据的地址
 * @param   dataBytes: 要读取的字节数
 * @return	读取的字节数
 */
int32_t RingBuf_Copy(ring_buffer_t* pRB, uint8_t* pdata, uint32_t dataBytes)
{
	return _prvRingBuf_Read(pRB, pdata, dataBytes, 0);
}

/**
 * @brief	从缓冲区取1个字节，会更新数据指针
 * @param	pRB	: 指向环形缓冲区的实例
 * @param	pData: 接收数据的地址
 * @return	1:成功 ; 0:失败
 * @remark  此功能会更新缓冲区，优先使用的逐字节读取方式
 */
int32_t RingBuf_Read1Byte(ring_buffer_t* pRB, uint8_t *pData)
{
	uint32_t ret = 0;
	if(!pRB )return ERROR;

	INIT_CRITICAL();
	ENTER_CRITICAL();
	if (pRB->cnt != 0)
	{
		pData[0] = pRB->pBuf[pRB->rNdx];
		pRB->rNdx = (pRB->rNdx + 1) % pRB->size;
		pRB->cnt--;
		ret = 1;
	}
	LEAVE_CRITICAL();
	return ret;
}

/**
 * @brief	获取环形缓冲区内最前面的数据地址
 * @param	pRB	:环形缓冲区实例
 * @param	ppData : 接收环形缓冲区地址的指针
 * @param   contiguous_bytes: 记录能读取的数据下标
 * @return	>=0:连续可读数据的下标 ; <0:失败
 * @remak   次功能只获取可连续读取数据的地址，性能优越，因为没有复制数据的操作。
 *          使用此功能后可以联合 Use RingBuf_Free() 来释放数据
 */
int32_t RingBuf_Peek(ring_buffer_t* pRB, uint8_t **ppData)
{
	uint32_t readToEnd = pRB->size - pRB->rNdx;
	uint32_t contiguous_Bytes;
	*ppData = &(pRB->pBuf[pRB->rNdx]);
	contiguous_Bytes = MIN(readToEnd, (readToEnd + pRB->wNdx) % pRB->size);
	return contiguous_Bytes;
}
/**
 * @brief	释放环形缓冲区内数据
 * @param	pRB	: 环形缓冲区实例
 * @param	bytesToFree : 释放的字节
 * @remak  可以联合 RingBuf_Peek() 使用
	更新计数值，其它不变，下次写入时继续从此位置。
 */
int32_t RingBuf_Free(ring_buffer_t* pRB, uint32_t bytesToFree)
{
	INIT_CRITICAL();
	ENTER_CRITICAL();
	pRB->rNdx = (pRB->rNdx + bytesToFree) % pRB->size;
	pRB->cnt -= bytesToFree;
	LEAVE_CRITICAL();
	return bytesToFree;
}
